package com.algorithm.dima.dap;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import com.problem.dima.DAPProblem;
import com.util.bean.CDGBean;
import com.util.random.RandomGenerator;
import com.util.solution.impl.DAPSolution;

public class CDG_Adap {
	private DAPProblem problem;
	private int run_num;
	/**
	 * to store the solution set of each sub-problem
	 */
	private HashMap<Integer, ArrayList<DAPSolution>> subProblemSet = new HashMap<Integer, ArrayList<DAPSolution>>();
	
	private CDGBean cdg = new CDGBean();
	
	private int max_num;
	
	public CDG_Adap(DAPProblem problem, int run_num){
		this.problem = problem;
		this.run_num = run_num;
		this.max_num = this.problem.sub_num;
		this.cdg.setK(10);
	}
	
	public void updateGridSystem(){
		for(int i=0;i<this.problem.obj_num;i++){
			cdg.idealPoint[i] = Integer.MAX_VALUE;
			cdg.nadPoint[i] = Integer.MIN_VALUE;
		}
		List<DAPSolution> list = this.problem.kdSet.getSolutions();
		for(DAPSolution s : list){
			//update nadir point
			for(int i=0;i<this.problem.obj_num;i++){
				if(this.cdg.nadPoint[i]<s.object_val[i])
					cdg.nadPoint[i] = (int) (s.object_val[i]*1.1);
				if(this.cdg.idealPoint[i]>s.object_val[i])
					cdg.idealPoint[i] = (int) (s.object_val[i]*0.9);
			}
		}
		//reset the width of each grid
		for(int i=0;i<this.problem.obj_num;i++)
			cdg.grid_width[i] = 1.0*(cdg.nadPoint[i] - cdg.idealPoint[i]) / cdg.K;
	}
	public void execute(int iteration){
		long startMili=System.currentTimeMillis();
		for(int i = 1;i<=iteration;i++){
			System.out.println("iteration of : "+i);
			if(i%20==0) this.cdg.updateKN(max_num);
			this.PLSexecute();
			updateGridSystem();
			try {
				this.rankBasedSelection();
			} catch (CloneNotSupportedException e) {
				e.printStackTrace();
			}
		}
		long endMili=System.currentTimeMillis();
		System.out.println((endMili-startMili)/1000+" s ");
		problem.kdSet.saveObjectiveResults("files/results/dima/dap/cdg/"+this.problem.devNum+"-"+this.problem.obj_num+".txt");
	}
	public void PLSexecute(){
		Iterator<DAPSolution> iter = problem.kdSet.getSolutions().iterator();
		List<DAPSolution> startList = new ArrayList<>();
		while(iter.hasNext()){
			DAPSolution s = iter.next();
			if(!s.isSearched()){
				try {
					s.setSearched(true);
					startList.add(s.clone());
				} catch (CloneNotSupportedException e) {
					e.printStackTrace();
				}
			}
		}
		//problem.initialCandidateList(startList);
		iter = startList.iterator();
		while(iter.hasNext()){
			this.generateNeighborhood(iter.next());
		}
	}
	/**
	 * generate the neighborhood of s
	 * @param s
	 */
	public void generateNeighborhood(DAPSolution node){
		int [] seq = new int[this.problem.devNum*this.problem.posNum];
	    int [] allocated = new int[this.problem.devNum*this.problem.posNum];
	    int [] curObjVal = new int[this.problem.obj_num];
	    int choseNum = 2;//chose two positions
	    int loop = 2;//the number of loops
	    ArrayList<Integer> oneSet = new ArrayList<Integer>();
	    for(int i=0;i<this.problem.posNum*this.problem.devNum;i++)
	        if(node.sequence[i]==1) oneSet.add(i);
	    int len = oneSet.size();
	    for(int iter=0;iter<loop;iter++){
	        int  [] randNum = RandomGenerator.permutation_array(0,len-1);
	        for(int i=0;i<len-choseNum;i+=2){
	        	seq	= node.sequence.clone();
	        	allocated = node.allocated.clone();
	        	curObjVal = node.object_val.clone();
	            int index1 = oneSet.get(randNum[i]);
	            int index2 = oneSet.get(randNum[i+1]);
	            seq[index1] = 0;
	            seq[index2] = 0;
	            curObjVal[0] -= this.problem.MassMatrix[index1];
	            curObjVal[0] -= this.problem.MassMatrix[index2];
	            curObjVal[1] -= this.problem.OICMatrix[index1];
	            curObjVal[1] -= this.problem.OICMatrix[index2];
	            int devIndex1 = index1 % this.problem.devNum;
	            int devIndex2 = index2 % this.problem.devNum;
	            int posIndex1 = index1 / this.problem.devNum;
	            int posIndex2 = index2 / this.problem.devNum;
	            this.problem.releaseRSC(allocated,devIndex1,posIndex1);
	            this.problem.releaseRSC(allocated,devIndex2,posIndex2);
	            for(int j = 0;j < this.problem.posNum;j ++){
	                for(int k = 0;k < this.problem.posNum;k ++){
	                    if(j==k && this.problem.segArray[devIndex1*this.problem.devNum+devIndex2]==1) continue;
	                    if(!this.problem.judgeRsc(allocated, devIndex1, j)
	                    && !this.problem.judgeSeg(seq, j*this.problem.devNum, devIndex1)
	                    && !this.problem.judgeRsc(allocated, devIndex2, k)
	                    && !this.problem.judgeSeg(seq, k*this.problem.devNum,devIndex2)){
	                        int [] tempObjVal = new int[2];
	                        tempObjVal[0] = curObjVal[0]+this.problem.MassMatrix[j*this.problem.devNum+devIndex1];
	                        tempObjVal[0] += this.problem.MassMatrix[k*this.problem.devNum+devIndex2];
	                        tempObjVal[1] = curObjVal[1]+this.problem.OICMatrix[j*this.problem.devNum+devIndex1];
	                        tempObjVal[1] += this.problem.OICMatrix[k*this.problem.devNum+devIndex2];
	                        if(tempObjVal[0]>=node.object_val[0] && tempObjVal[1]>=node.object_val[1])
	                            continue;
	                        DAPSolution newNode = new DAPSolution(this.problem.obj_num, -1);
	                        newNode.sequence = seq.clone();
	                        newNode.sequence[j*this.problem.devNum+devIndex1]=1;
	                        newNode.sequence[k*this.problem.devNum+devIndex2]=1;
	                        newNode.object_val = tempObjVal.clone();
	                        newNode.allocated = allocated.clone();
	                        for(int m=0;m<this.problem.rscNum;m++){
	                            newNode.allocated[m*this.problem.posNum+j]+=this.problem.devRscArray[m*this.problem.devNum+devIndex1];
	                            newNode.allocated[m*this.problem.posNum+k]+=this.problem.devRscArray[m*this.problem.devNum+devIndex2];
	                        }
	                        problem.kdSet.add(newNode);
	                    }
	                }
	            }
	        }
	    }
	}
	public int index = 0;
	public void rankBasedSelection() throws CloneNotSupportedException{
		if(this.problem.kdSet.getPopSize()<=cdg.N) return;
		this.subProblemSet.clear();
		List<DAPSolution> list = this.problem.kdSet.getSolutions();
		for(DAPSolution s : list){
			for(int i=0;i<this.problem.obj_num;i++){
				int coord = (int) ((s.object_val[i]-cdg.idealPoint[i])/cdg.grid_width[i]);
				if(coord == cdg.K) coord = cdg.K - 1;
				if(subProblemSet.keySet().contains(i*cdg.K+coord)){
					subProblemSet.get(i*cdg.K+coord).add(s);
				}else{
					ArrayList<DAPSolution> t = new ArrayList<>();
					t.add(s);
					subProblemSet.put(i*cdg.K+coord, t);
				}
			}
		}
		for(int i=0;i<this.problem.obj_num;i++){
			index = i;
			for(int d = 0;d < cdg.K; d++){
				List<DAPSolution> cur_list = this.subProblemSet.get(i*cdg.K + d);
				if(cur_list==null) continue;
				cur_list.sort(new Comparator<DAPSolution>() {
					@Override
					public int compare(DAPSolution o1, DAPSolution o2) {
						if(o1.object_val[1-index]>o2.object_val[1-index]){
							return 1;
						}else if(o1.object_val[1-index]<o2.object_val[1-index]){
							return -1;
						}
						return 0;
					}
				});
				int cur = 0;
				for(DAPSolution s : cur_list) s.gridIndex[1-index] = cur++;
			}
		}
		List<DAPSolution> solutions = list;//this.problem.kdSet.getSolutions();
		//sort to obtain R'
		for(DAPSolution s : solutions) Arrays.sort(s.gridIndex,0,this.problem.obj_num);
		this.fastLexSort(solutions);
		this.problem.kdSet.clear();
//		for(int i=0;i<cdg.N;i++) {
//			this.problem.kdSet.add(solutions.get(i));
//		}
		for(DAPSolution s : solutions){
			if(s.gridIndex[0]==0){
				this.problem.kdSet.add(s);
			}
		}
		this.cdg.N = this.problem.kdSet.getPopSize();
//		for(DAPSolution s : solutions){
//			if(s.gridIndex[0]==0){
//				this.problem.kdSet.add(s);
//			}
//		}
	}
	/**
	 * a fast lex sort for 2 objectives
	 * @param list
	 */
	public void fastLexSort(List<DAPSolution> list ){
		list.sort(new Comparator<DAPSolution>(){
			public int compare(DAPSolution o1, DAPSolution o2){
				for(int i=0;i<problem.obj_num;i++){
					if(o1.gridIndex[i] > o2.gridIndex[i]) return 1;
					if(o1.gridIndex[i] < o2.gridIndex[i]) return -1;
				}
				return 0;
			}
		});
	}
	public static void main(String []args){
		for(int i=0;i<1;i++){
			DAPProblem tsp = new DAPProblem();
			new CDG_Adap(tsp, i).execute(1000);
		}
	}
}
